﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using System.Windows.Controls;

namespace FxArchitect.ContentPipeline
{
    public class ContentExtensionCollection : KeyedCollection<string, ContentExtensionProject>
    {
        protected override string GetKeyForItem(ContentExtensionProject item)
        {
            return item.Name;
        }

        public ContentImporterInfo FindImporterByFileExtension(string extension)
        {
            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < this[i].Importers.Count; j++)
                {
                    foreach (string ext in this[i].Importers[j].FileExtensions)
                    {
                        if (string.Compare(ext, extension, true) == 0)
                            return this[i].Importers[j];
                    }
                }
            }
            return null;
        }
        public ContentImporterInfo FindImporterByTypeName(string typeName)
        {
            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < this[i].Importers.Count; j++)
                {
                    if (this[i].Importers[j].TypeName == typeName)
                        return this[i].Importers[j];
                }
            }
            return null;
        }
        public ContentImporterInfo FindImporterByDisplayName(string displayName)
        {
            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < this[i].Importers.Count; j++)
                {
                    if (this[i].Importers[j].DisplayName == displayName)
                        return this[i].Importers[j];
                }
            }
            return null;
        }

        public ContentProcessorInfo FindProcessorByTypeName(string typeName)
        {
            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < this[i].Processors.Count; j++)
                {
                    if (this[i].Processors[j].TypeName == typeName)
                        return this[i].Processors[j];
                }
            }
            return null;
        }
        public ContentProcessorInfo FindProcessorByDisplayName(string displayName)
        {
            for (int i = 0; i < Count; i++)
            {
                for (int j = 0; j < this[i].Processors.Count; j++)
                {
                    if (this[i].Processors[j].DisplayName == displayName)
                        return this[i].Processors[j];
                }
            }
            return null;
        }
    }

    public class ContentExtensionProject
    {
        const string CONTENT_REF = "Content References";
        const string IIMPORTER = "IContentImporter", IPROCESSOR = "IContentProcessor";
        const string XNA_VERSION = ", Version=4.0.0.0, PublicKeyToken=842cf8be1de50553";
        static string XNA_FOLDER = Environment.GetEnvironmentVariable("XNAGSv4") + "\\References\\Windows\\x86";
        static string[] ms_pipelineAssemblies =
        {
            "Microsoft.Xna.Framework.Content.Pipeline",
            "Microsoft.Xna.Framework.Content.Pipeline.EffectImporter",
            "Microsoft.Xna.Framework.Content.Pipeline.FBXImporter",
            "Microsoft.Xna.Framework.Content.Pipeline.TextureImporter",
            "Microsoft.Xna.Framework.Content.Pipeline.XImporter",
            "Microsoft.Xna.Framework.Content.Pipeline.VideoImporters",
            "Microsoft.Xna.Framework.Content.Pipeline.AudioImporters"
        };

        string m_filename, m_name;
        Assembly m_assembly;
        ContentImporterInfoCollection m_importers;
        ContentProcessorInfoCollection m_processors;

        [DisplayName("Assembly Name")]
        [ReadOnly(true)]
        public string Name { get { return m_name; } }

        [DisplayName("Full Path")]
        [ReadOnly(true)]
        public string Filename { get { return m_filename; } }

        [Browsable(false)]
        public ContentImporterInfoCollection Importers { get { return m_importers; } }

        [Browsable(false)]
        public ContentProcessorInfoCollection Processors { get { return m_processors; } }

        public ContentExtensionProject(string filename)
        {
            m_filename = filename;
            m_importers = new ContentImporterInfoCollection();
            m_processors = new ContentProcessorInfoCollection();

            // if the extension is a project file, compile it first
            if (m_filename.EndsWith(".csproj"))
                CompileAssembly();

            // if it's an xna pipeline assembly, search for it in the xna folder
            for (int i = 0; i < ms_pipelineAssemblies.Length; i++)
            {
                if (m_filename == ms_pipelineAssemblies[i])
                    m_filename = Path.Combine(XNA_FOLDER, m_filename + ".dll");
            }

            // should be able to load the assembly now
            m_assembly = Assembly.LoadFrom(m_filename);
            foreach (Type type in m_assembly.GetExportedTypes())
            {
                if (type.IsAbstract || type.IsInterface)
                    continue;

                // skip items that are marked as not visible in design time
                object[] dtvis = type.GetCustomAttributes(typeof(DesignTimeVisibleAttribute), false);
                if (dtvis != null && dtvis.Length > 0)
                {
                    DesignTimeVisibleAttribute dtattr = dtvis[0] as DesignTimeVisibleAttribute;
                    if (!dtattr.Visible) continue;
                }

                if (type.GetInterface(IIMPORTER) != null)
                {
                    object[] attrs = type.GetCustomAttributes(typeof(ContentImporterAttribute), false);
                    if (attrs != null && attrs.Length > 0)
                    {
                        ContentImporterAttribute attr = attrs[0] as ContentImporterAttribute;
                        m_importers.Add(new ContentImporterInfo(attr, type));
                    }
                }
                else if (type.GetInterface(IPROCESSOR) != null)
                {
                    object[] attrs = type.GetCustomAttributes(typeof(ContentProcessorAttribute), false);
                    if (attrs != null && attrs.Length > 0)
                    {
                        ContentProcessorAttribute attr = attrs[0] as ContentProcessorAttribute;
                        m_processors.Add(new ContentProcessorInfo(attr, type));
                    }
                }
            }

            m_name = m_assembly.FullName.Substring(0, m_assembly.FullName.IndexOf(','));

            //m_root = new TreeNode(m_name);
            //m_root.Tag = this;
            //m_root.ImageKey = m_root.SelectedImageKey = "ReferenceIcon.bmp";
            //m_root.ContextMenuStrip = MainForm.Reference_RemoveMenuStrip;
        }

        /// <summary>
        /// Compiles a csproj file and copies the resulting library into the program's content
        /// reference folder so that it can be used.
        /// </summary>
        void CompileAssembly()
        {
            Project project = new Project(m_filename);

            BuildParameters buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection);
            buildParameters.Loggers = new ILogger[0];

            BuildManager.DefaultBuildManager.BeginBuild(buildParameters);
            BuildRequestData request = new BuildRequestData(project.CreateProjectInstance(), new string[0]);
            BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(request);

            submission.ExecuteAsync(null, null);
            submission.WaitHandle.WaitOne();

            BuildManager.DefaultBuildManager.EndBuild();

            FileInfo fileInfo = new FileInfo(m_filename);
            string libraryFilename = Path.GetFileNameWithoutExtension(fileInfo.Name) + ".dll";
            string libraryPath = Path.Combine(fileInfo.Directory.FullName,
                                              project.GetPropertyValue("OutputPath"),
                                              libraryFilename);

            string newDirectory = new FileInfo(Assembly.GetCallingAssembly().Location).Directory.FullName;
            m_filename = Path.Combine(newDirectory, CONTENT_REF);

            if (!Directory.Exists(m_filename))
                Directory.CreateDirectory(m_filename);
            m_filename = Path.Combine(m_filename, libraryFilename);

            File.Copy(libraryPath, m_filename, true);
        }

        void ctx_remove_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            TreeViewItem node = ((sender as MenuItem).Parent as ContextMenu).PlacementTarget as TreeViewItem;
            ContentProject contentproj = null;
            while (node != null)
            {
                contentproj = node.Tag as ContentProject;
                if (contentproj != null)
                {
                    contentproj.Select();
                    break;
                }
                node = node.Parent as TreeViewItem;
            }

            if (contentproj != null)
                contentproj.RemoveExtension(this);
        }

        ContextMenu CreateContextMenu()
        {
            ContextMenu menu = new ContextMenu();

            MenuItem remove = new MenuItem();
            remove.Header = "Remove";
            remove.Click += new System.Windows.RoutedEventHandler(ctx_remove_Click);
            menu.Items.Add(remove);

            return menu;
        }

        public TreeViewItem CreateTreeNode()
        {
            TreeViewItem node = new TreeViewItem();
            node.Header = m_name;
            node.Tag = this;
            node.ContextMenu = CreateContextMenu();
            return node;
        }
        //public TreeNode CreateTreeNode()
        //{
        //    TreeNode node = new TreeNode(m_assembly.FullName.Substring(0, m_assembly.FullName.IndexOf(',')));
        //    node.ImageKey = "ReferenceIcon.bmp";
        //    node.SelectedImageKey = "ReferenceIcon.bmp";
        //    return node;
        //}

        /// <summary>
        /// Writes a reference element to a project configuration for this assembly.
        /// </summary>
        /// <param name="project"></param>
        public ProjectItem WriteReference(Project project)
        {
            if (m_filename == ms_pipelineAssemblies[0])
                return null;

            for (int i = 0; i < ms_pipelineAssemblies.Length; i++)
            {
                if (m_filename == ms_pipelineAssemblies[i])
                {
                    return project.AddItem("Reference", ms_pipelineAssemblies[i] + XNA_VERSION)[0];
                }
            }

            ProjectItem item = project.AddItem("Reference", m_assembly.FullName)[0];
            item.SetMetadataValue("HintPath", m_filename);
            return item;
        }
    }
}
